C语言未定义行为陷阱与规避方法

📚 共计 30 章节
01
未定义行为初探
什么是未定义行为?为什么C语言中会有未定义行为?未定义行为、未指定行为、实现定义行为的区别。
概念基础
02
指针的迷魂阵
解引用空指针、解引用野指针、指针算术越界、指向局部变量的指针。
指针危险
03
数组的边界之外
数组下标越界访问、多维数组的越界、变长数组(VLA)的陷阱。
数组越界
04
整数的疯狂世界
有符号整数溢出、无符号整数回绕、移位操作符的未定义行为、负数的移位。
整数溢出
05
类型双关与别名规则
违反严格别名规则、通过不同类型的指针访问对象、union在类型双关中的正确使用。
类型别名
06
const与volatile的陷阱
修改const变量、忽略volatile的优化问题、const指针的转换。
限定符优化
07
序列点与副作用
序列点是什么?在两个序列点之间多次修改变量、函数参数的求值顺序、&&和||的短路求值。
序列点求值
08
函数与作用域
函数声明与定义不匹配、隐式函数声明、main函数的特殊规则、递归的栈溢出。
函数作用域
09
字符串的暗礁
字符串字面量不可修改、字符串缺少空字符终止、strcpy和strcat的缓冲区溢出。
字符串缓冲区
10
动态内存管理
malloc/calloc/realloc的使用错误、内存泄漏、双重释放、释放后使用(UAF)。
内存泄漏
11
结构体与联合体
结构体填充和对齐、结构体比较的陷阱、联合体的未定义行为、位域的跨平台问题。
结构体联合体
12
标准库函数陷阱
printf/scanf格式字符串不匹配、gets函数的危险、setjmp/longjmp的未定义行为。
标准库陷阱
13
浮点数的迷雾
浮点数比较的陷阱、NaN和Infinity的处理、浮点数运算的精度问题。
浮点精度
14
预处理器的魔法
宏定义中的副作用、宏与函数的区别、条件编译的陷阱、#和##运算符。
预处理器
15
多文件编程
头文件重复包含、全局变量的定义与声明、static和extern的用法、链接顺序问题。
多文件链接
16
并发与竞态
数据竞争、volatile在多线程中的局限性、原子操作与内存序、线程安全函数。
并发线程
17
信号处理
信号处理函数中可调用的函数列表、volatile sig_atomic_t、信号的异步安全。
信号异步
18
setjmp与longjmp
正确使用setjmp/longjmp、自动变量的值、longjmp与信号处理的交互。
跳转异常
19
断言与错误处理
assert的副作用、错误码与errno、setjmp/longjmp作为异常处理。
断言错误
20
编译器优化与未定义行为
编译器如何利用未定义行为优化、-O2/-O3下的诡异bug、volatile的作用。
优化编译器
21
整数提升与类型转换
整数提升规则、隐式类型转换的陷阱、有符号与无符号的比较。
类型转换提升
22
位操作的艺术
位域的实现定义行为、移位操作的未定义行为、位操作的跨平台问题。
位操作位域
23
内联汇编
内联汇编的约束条件、破坏列表、内联汇编与优化器的交互。
汇编内联
24
属性与扩展
GCC __attribute__的使用、__builtin_expect、__restrict关键字。
GCC扩展
25
调试未定义行为
使用-fsanitize=undefined、Valgrind、静态分析工具、代码审查技巧。
调试工具
26
嵌入式系统中的未定义行为
volatile在嵌入式中的正确使用、内存映射寄存器、中断服务程序。
嵌入式寄存器
27
安全编码规范
MISRA C、CERT C编码标准、安全编码的最佳实践。
规范安全
28
历史遗留问题
gets函数、strcpy/strcat、sprintf、atoi等不安全函数的替代。
遗留替代
29
C11/C17/C23新特性
_Generic泛型选择、_Static_assert、_Alignas/_Alignof、内存模型。
新标准C23
30
总结与实战
常见未定义行为速查表、代码审查清单、构建安全C代码的流程。
总结实战